/** @file    Message.cpp
 *  @author  Alessandro Polo
 *  @version $Id: Message.cpp 2838 2010-08-03 11:14:42Z alex $
 *  @brief
 * File containing methods for the wosh::Message class.
 * The header for this class can be found in Message.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/Message.h>
 
 #include <core/ObjectAllocator.h>
 #include <core/Object.h>
 #include <core/Bundle.h>
 #include <core/PropertiesProvider.h>
 #include <core/InterfacesProvider.h>

 #include <core/DataModels.h>
 #include <core/Utilities.h>
 #include <core/SecurityManager.h>
 #include <cstring>


using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::Message, "wosh::ISerializable", 1.01, _static_Message)

 WOSH_REGISTER(wosh::MessageNetworkForwarding, "wosh::ISerializable", 1.01, _static_MessageNetworkForwarding)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

Message::Message() {
	this->content = NULL;
	this->security = NULL;
	this->forwarders = NULL;
	this->contentDelete = true;
	clear();
	this->id = Utilities::randomInt64();
	this->id = _GET_Abs(this->id);

 }

Message::Message( MessageContent* Content, bool deleteContents ) {
	this->content = NULL;
	this->security = NULL;
	this->forwarders = NULL;
	this->contentDelete = true;
	clear();
	this->id = Utilities::randomInt64();
	this->id = _GET_Abs(this->id);
	this->content = Content;
	this->contentDelete = deleteContents;
 }

Message::Message( const std::string& DestinationURI, MessageContent* Content, bool deleteContents ) {
	this->content = NULL;
	this->security = NULL;
	this->forwarders = NULL;
	this->contentDelete = true;
	clear();
	this->id = Utilities::randomInt64();
	this->id = _GET_Abs(this->id);
	this->content = Content;
	this->contentDelete = deleteContents;

	this->destination.fromString(DestinationURI);
 }

Message::Message( const Message& m ) {
	this->content = NULL;
	this->security = NULL;
	this->forwarders = NULL;
	this->contentDelete = true;
	*this = m;
 }

Message::~Message() {
	if ( this->contentDelete )
		freeContent();

	if ( this->security != NULL ) 
		delete this->security; this->security = NULL;
	if ( this->forwarders != NULL ) 
		delete this->forwarders; this->forwarders = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Message::clear() {
	this->id = 0;
	this->ref_id = 0;

	this->timestamp = Utilities::std_time();
	this->TTL = 0;

	this->sourceBus = "";
	this->currentBus = "";
	this->destinationBus = "";

	this->source.clear();
	this->destination.clear();

	this->sourceUUID = "";
	this->destinationUUID = "";

	this->priority = Message::PRIORITY_NORMAL;
	this->acknowledged = 0;

	if ( this->contentDelete )
		freeContent();

	if ( this->security != NULL )
		delete this->security; this->security = NULL;

	if ( this->forwarders != NULL )
		delete this->forwarders; this->forwarders = NULL;

 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Message::freeContent() {
	if ( this->content != NULL )
		delete this->content;
	this->content = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// SETTERS

void Message::setContent( MessageContent* Content, bool deleteContents ) {
	if ( this->contentDelete )
		freeContent();
	this->content = Content;
	this->contentDelete = deleteContents;
 }

void Message::setSecurityToken( SecurityToken* st ) {
	if ( this->security != NULL )
		delete this->security;
	this->security = st;
 }

void Message::setSource( const Object* object ) {
	if ( object == NULL ) {
		this->source.clear();
		this->sourceUUID.clear();
		return;
	 }
	this->source = object->getURI();
	this->sourceUUID = object->getEntityID();
 }

void Message::setDestinationBroadcast( bool anykernel ) {
	this->destination.setAny();
	if ( anykernel )
		this->destination.setKernelAny();
	else
		this->destination.setKernelLocal();
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// SETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// GETTERS

std::string Message::getTimestampAsString() const {
	return Utilities::getTimeStampUTF(this->timestamp);
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// GETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS

Message& Message::operator=( const Message& m ) {
	if (this == &m) return *this; // same object?

	this->id = m.id;
	this->ref_id = m.ref_id;

	this->timestamp = m.timestamp;
	this->TTL = m.TTL;

	this->sourceBus = m.sourceBus;
	this->destinationBus = m.destinationBus;

	this->source = m.source;
	this->destination = m.destination;

	this->sourceUUID = m.sourceUUID;
	this->destinationUUID = m.destinationUUID;

	this->acknowledged = m.acknowledged;

	if ( this->contentDelete )
		freeContent();
	if ( m.content == NULL)
		this->content = NULL;
	else
		this->content = m.content->clone();

	if ( this->security != NULL )
		delete this->security; this->security = NULL;
	if ( m.security != NULL)
		this->security = m.security->clone();

	if ( this->forwarders != NULL )
		delete this->forwarders; this->forwarders = NULL;
	if ( m.forwarders != NULL)
		this->forwarders = m.forwarders->clone();

	return *this;
 }

bool Message::operator==( const Message& other ) const {
	if ( this->id != other.id || this->timestamp != other.timestamp )
		return false;

	if ( this->source != other.source ) return false;
	if ( this->destination != other.destination ) return false;

	if ( this->content == NULL && other.content == NULL ) return true;
	if ( this->content == NULL || other.content == NULL ) return false;

	if ( this->content->getType() != other.content->getType() )
		return false;

	///@bug to be fully implemented

	return true;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
/*
FONDERE FIELD E DATAMODEL!

*/
class Field {
	public:
		Field() {}
		virtual ~Field() {}
	public:
//name
//type
//ptrSet
//ptrGet
};

class Field_Variant : public Field {
	public:
		Field_Variant() : Field() {}
		virtual ~Field_Variant() {}
	public:

};


#define _FIELD_CREATE(FIELD_NAME, OBJECT_TYPE)				\
	else if ( strcmp(field_name,FIELD_NAME)==0 ) {			\
		value = new OBJECT_TYPE();							\
	 }

#define _FIELD_CREATE_VARIANT(FIELD_NAME, VARIANT_TYPE)		\
	else if ( strcmp(field_name,FIELD_NAME)==0 ) {			\
		value = new wosh::Variant(Variant::VARIANT_TYPE);	\
	 }

#define _FIELD_VARIANT_GET(FIELD_NAME, FIELD_REF, OBJTYPE)												\
	if ( strcmp(field_name,FIELD_NAME)==0 ) {															\
		if ( !value->isKindOf<wosh::Variant>() ) return WRET_ERR_ILLEGAL_USE;							\
		if ( value->as<wosh::Variant>()->getType() == Variant::OBJTYPE ) return WRET_ERR_ILLEGAL_USE;	\
		FIELD_REF = *value->as<wosh::Variant>();															\
	 )

#define _Message_FIELD_SourceBus		"SourceBus"
#define _Message_FIELD_CurrentBus		"CurrentBus"
#define _Message_FIELD_DestinationBus	"DestinationBus"
#define _Message_FIELD_Source			"Source"
#define _Message_FIELD_Destination		"Destination"
#define _Message_FIELD_SourceUUID		"SourceUUID"
#define _Message_FIELD_DestinationUUID	"DestinationUUID"


WRESULT Message::listFields( std::vector<std::string>& fields_name, const char* parent_field_name, const SecurityToken* token ) {
	WRESULT ret = WRET_OK;
	if ( parent_field_name == NULL || strlen(parent_field_name) == 0 ) {
		fields_name.push_back(_Message_FIELD_SourceBus);
		fields_name.push_back(_Message_FIELD_CurrentBus);
		fields_name.push_back(_Message_FIELD_DestinationBus);
		fields_name.push_back(_Message_FIELD_Source);
		fields_name.push_back(_Message_FIELD_Destination);
		fields_name.push_back(_Message_FIELD_SourceUUID);
		fields_name.push_back(_Message_FIELD_DestinationUUID);
	 }

	return ret;
 }

WRESULT Message::getField( const char* field_name, ISerializable* value, const SecurityToken* token ) {
	WRESULT ret = WRET_OK;

//	DataFieldCollector<Message>* fields = getDataFields();
//	value = fields[0]->extract(this);

	return ret;
 }

WRESULT Message::setField( const char* field_name, const ISerializable* value, const SecurityToken* token ) {
	if ( field_name == NULL ) return WRET_ERR_PARAM;
	WRESULT ret = WRET_OK;

//	_FIELD_VARIANT_GET(_Message_FIELD_SourceBus, this->sourceBus, STRING)
	//if ( strcmp(field_name,_Message_FIELD_SourceBus)==0 ) {
	//	if ( !value->isKindOf<wosh::Variant>() ) return WRET_ERR_ILLEGAL_USE;
	//	if ( value->as<wosh::Variant>()->getType() == Variant::STRING ) return WRET_ERR_ILLEGAL_USE;
	//	this->sourceBus = value->as<wosh::Variant>()->asString();
	// }

	if ( strcmp(field_name,_Message_FIELD_SourceBus)==0 ) {
		if ( value->isKindOf<Variant>() && value->as<Variant>()->isString() )
			this->sourceBus = value->as<Variant>()->asString();
		else
			return WRET_ERR_ILLEGAL_USE;
	 }
	else if ( strcmp(field_name,_Message_FIELD_CurrentBus)==0 ) {
		if ( value->isKindOf<Variant>() && value->as<Variant>()->isString() )
			this->sourceBus = value->as<Variant>()->asString();
		else
			return WRET_ERR_PARAM;
	 }
	else if ( strcmp(field_name,_Message_FIELD_DestinationBus)==0 ) {
		if ( value->isKindOf<Variant>() && value->as<Variant>()->isString() )
			this->destinationBus = value->as<Variant>()->asString();
		else
			return WRET_ERR_PARAM;
	 }
	//else if ( strcmp(field_name,_Message_FIELD_Source)==0 )
	//	this->source.replaceField("", value);
	//else if ( key.find("Source.") == 0 )
	//	this->source.replaceField(key.substr(7), value);

	//else if ( key == "Destination" )
	//	this->destination.replaceField("", value);
	//else if ( key.find("Destination.") == 0 )
	//	this->destination.replaceField(key.substr(12), value);

	else if ( strcmp(field_name,_Message_FIELD_SourceUUID)==0 ) {
		if ( value->isKindOf<Variant>() && value->as<Variant>()->isString() )
			this->sourceUUID = value->as<Variant>()->asString();
		else
			return WRET_ERR_ILLEGAL_USE;
	 }
	else if ( strcmp(field_name,_Message_FIELD_DestinationUUID)==0 ) {
		if ( value->isKindOf<Variant>() && value->as<Variant>()->isString() )
			this->destinationUUID = value->as<Variant>()->asString();
		else
			return WRET_ERR_ILLEGAL_USE;
	 }

	return ret;
 }

ISerializable* Message::createObjectForField( const char* field_name, const SecurityToken* token ) {
	ISerializable* value = NULL;
	if ( field_name == NULL ) return NULL;
	_FIELD_CREATE_VARIANT(_Message_FIELD_SourceBus, STRING)
	_FIELD_CREATE_VARIANT(_Message_FIELD_CurrentBus, STRING)
	_FIELD_CREATE_VARIANT(_Message_FIELD_DestinationBus, STRING)
	_FIELD_CREATE_VARIANT(_Message_FIELD_SourceBus, STRING)
	_FIELD_CREATE(_Message_FIELD_Source, wosh::URI)
	_FIELD_CREATE(_Message_FIELD_Destination, wosh::URI)
	_FIELD_CREATE_VARIANT(_Message_FIELD_DestinationUUID, STRING)

	return value;
 }

////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
////////////////////////////////////////////////////////////////////////////////////////////////////// FIELDS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Message* Message::replyTo(  MessageContent* newContent, SecurityToken* security_payload ) const {
	Message* reply = new Message( newContent );

	reply->ref_id = this->id;

	reply->sourceBus = "";
	reply->destinationBus = this->sourceBus;

	reply->source = this->destination;
	reply->destination = this->source;

	reply->sourceUUID = this->destinationUUID;
	reply->destinationUUID = this->sourceUUID;

	reply->security = security_payload;
	if ( reply->security == NULL && this->security != NULL )
		reply->security = this->security->clone();

	return reply;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
bool Message::isMessageForBundle( const Message& message, const Bundle* bundle ) {
	if ( bundle == NULL ) return false;
	return isMessageTarget( message, bundle->getURI(), bundle->bundleProperties(), bundle->bundleInterfaces() );
 }

bool Message::isMessageTarget( const Message& message, const URI& itemURI, const PropertiesProvider* properties, const InterfacesProvider* interfaces )
 {
//	int uriDiff = URI::pregDistance( message.getDestination(), itemURI );

	bool uriOk = URI::pregMatch( message.getDestination(), itemURI );
	if ( !uriOk ) return false;
	if ( !message.getDestination().hasExpression() )
		return true;


return false;
 }*/

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<Message>* Message::getDataFields() {
	DataFieldCollector<Message>* fields = new DataFieldCollector<Message>();
	fields->add<int64, Variant>(&Message::getID, "ID", 0);
	fields->add<int64, Variant>(&Message::getReferenceID, "RefID", 1);
	fields->add<int64, Variant_DT>(&Message::getTimestamp, "TimeStamp", 2);
	fields->add<const URI&, URI>(&Message::getSource, "Source", 3);
	fields->add<const URI&, URI>(&Message::getDestination, "Destination", 4);
	fields->add<const std::string&, Variant>(&Message::getSourceBus, "SourceBus", 5);
	fields->add<const std::string&, Variant>(&Message::getCurrentBus, "CurrentBus", 6);
	fields->add<const std::string&, Variant>(&Message::getDestinationBus, "DestinationBus", 7);
	fields->add<const std::string&, Variant>(&Message::getSourceUUID, "SourceUUID", 8);
	fields->add<const std::string&, Variant>(&Message::getDestinationUUID, "DestinationUUID", 9);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string Message::toString() const {
	return Utilities::format("%s#%"PRId64, getClassNameOnlyOf(getClassName()), this->id );
 }

bool Message::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->id ) & ret;
	ret = hash_context->update( this->ref_id ) & ret;
	ret = hash_context->update( this->timestamp ) & ret;
	ret = hash_context->update( this->priority ) & ret;
	ret = hash_context->update( this->sourceBus ) & ret;
	ret = hash_context->update( this->currentBus ) & ret;
	ret = hash_context->update( this->destinationBus ) & ret;
	ret = this->source.updateHash(hash_context) & ret;
	ret = this->destination.updateHash(hash_context) & ret;
	ret = hash_context->update( this->sourceUUID ) & ret;
	ret = hash_context->update( this->destinationUUID ) & ret;
	if ( this->content != NULL )
		ret = this->content->updateHash(hash_context) & ret;
//	if ( this->security != NULL )
//		ret = this->security->updateHash(hash_context) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void message_assign( Message* message, int64 id, int64 ref_id, int64 ts ) {
	if ( message == NULL ) return;
	message->id = id;
	message->ref_id = ref_id;
	message->timestamp = ts;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
